package main;

import java.awt.BasicStroke;
import java.awt.Button;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Label;
import java.awt.Panel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.util.ArrayList;

public class HeroicMoveUI extends MoveUI {
	public HeroicMoveMouse myMouse;
	public Panel targetStats;
	
	public static enum choosingHeroes {FIRST, COUNTER, MULTIPLE, DONE, NONE};
	public choosingHeroes choosingHero;
	public int selectingArmy = 0;
	
	public Button callHeroicMove;
	public Button skipHeroicMove;
	
	public Button counterCall;
	public Button skipCounter;
	
	public boolean bothChoosing;
	public Button doneChoosing;
	
	public Button doneMoving;
	
	public Hero currentHero;
	public ArrayList<Hero> callingHeroes;
	public ArrayList<Unit> movingUnits;
	
	public void beginPhase() {
		myUI.selectedUnit = null;
		myUI.targetedUnit = null;
		myUI.endPhase.setEnabled(false);
		
		currentHero = null;
		callingHeroes = new ArrayList<Hero>();
		movingUnits = new ArrayList<Unit>();
		
		choosingHero = HeroicMoveUI.choosingHeroes.NONE;
		
		if (!this.heroicMovePossible((myUI.game.priority+1)%2)) {
			this.endPhase();
			return;
		}
		
		
		bothChoosing = true;
		selectingArmy = (myUI.game.priority+1)%2;
		
		myMouse = new HeroicMoveMouse();
		myMouse.myUI = myUI;
		myMouse.myMoveUI = this;
		myUI.addMouseListener(myMouse);
		myUI.addMouseMotionListener(myMouse);
		
		targetStats = myUI.buildStatsPanel("Target", myUI.dim.width-200, myUI.dim.height-280);
		myUI.add(targetStats);
		
		
		callHeroicMove = new Button("Call Heroic Move");
		callHeroicMove.setBounds(myUI.dim.width-100,50,100,50);
		callHeroicMove.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				callHeroicMove.setVisible(false);
				skipHeroicMove.setVisible(false);
				UI.messageLog.add(Game.armyTitles[selectingArmy] + ", select a hero to call the move");
				choosingHero = HeroicMoveUI.choosingHeroes.FIRST;
				myUI.repaint();
			}
		});
		myUI.add(callHeroicMove);
		
		
		skipHeroicMove = new Button("Skip Heroic Move");
		skipHeroicMove.setBounds(myUI.dim.width-100,100,100,50);
		skipHeroicMove.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				endPhase();
			}
		});
		myUI.add(skipHeroicMove);
		
		
		counterCall = new Button("Counter call?");
		counterCall.setBounds(myUI.dim.width-100,50,100,50);
		counterCall.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				UI.messageLog.add(Game.armyTitles[selectingArmy] + ", select a hero to counter the call");
				choosingHero = HeroicMoveUI.choosingHeroes.COUNTER;
				myUI.repaint();
			}
		});
		myUI.add(counterCall);
		counterCall.setVisible(false);
		
		skipCounter = new Button("Skip counter");
		skipCounter.setBounds(myUI.dim.width-100, 100, 100, 50);
		skipCounter.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				counterCall.setVisible(false);
				skipCounter.setVisible(false);
				
				int newSelectingArmy = (selectingArmy+1)%2;
				
				// If the other guys can add more callers, let them
				if (heroicMovePossible(newSelectingArmy)) {
					selectingArmy = newSelectingArmy;
					UI.messageLog.add(Game.armyTitles[selectingArmy] + ", choose another hero to call or select done");
					choosingHero = HeroicMoveUI.choosingHeroes.MULTIPLE;
					bothChoosing = false;
					doneChoosing.setVisible(true);
				}
				
				// Otherwise, just be done
				else {
					UI.messageLog.add(Game.armyTitles[newSelectingArmy] + " is out of heroes that can call a move");
					choosingHero = HeroicMoveUI.choosingHeroes.DONE;
					doneMoving.setVisible(true);
					advanceMovers();
				}
				myUI.repaint();
			}
		});
		myUI.add(skipCounter);
		skipCounter.setVisible(false);
		
		
		doneChoosing = new Button("Done choosing");
		doneChoosing.setBounds(myUI.dim.width-100, 50, 100, 50);
		doneChoosing.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				int newSelectingArmy = (selectingArmy+1)%2;
				// If both were choosing, advance to other army and notify that both aren't anymore
				if (bothChoosing) {
					bothChoosing = false;
					selectingArmy = newSelectingArmy;
					// If we also don't have any more heroes to call, end it
					if (!heroicMovePossible(selectingArmy)){
						UI.messageLog.add(Game.armyTitles[selectingArmy] + " is out of heroes that can call a move");
						choosingHero = HeroicMoveUI.choosingHeroes.DONE;
						doneMoving.setVisible(true);
						advanceMovers();
					}
				}
				// Otherwise, we're done choosing for good and we can start moving
				else {
					choosingHero = HeroicMoveUI.choosingHeroes.DONE;
					doneMoving.setVisible(true);
					advanceMovers();
				}
				myUI.repaint();
			}
		});
		myUI.add(doneChoosing);
		doneChoosing.setVisible(false);
		
		doneMoving = new Button("Done Moving");
		doneMoving.setBounds(myUI.dim.width-100,50,100,50);
		doneMoving.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				advanceMovers();
				myUI.repaint();
			}
		});
		myUI.add(doneMoving);
		doneMoving.setVisible(false);
		
		
		
		myUI.revalidate();
		myUI.repaint();
	}
	
	private boolean heroicMovePossible(int armyIndex) {
		for (Unit u : myUI.game.armies[armyIndex].units){
			if (u instanceof Hero && u.mightLeft > 0 && 
					u.battle == null && !callingHeroes.contains((Hero)u)
					&& !u.isTransfixed)
				return true;
		}
		return false;
	}
	
	
	public void addFirstCaller(Hero hero) {
		// Assumption is that the Heroic move buttons are already hidden

		UI.messageLog.add(hero.toChoiceString() + " is calling a heroic move!");
		// Add him to the calling heroes list
		callingHeroes.add(hero);
		
		// If we can counter call, enable the buttons and set choosingHero to none
		int newSelectingArmy = (this.selectingArmy+1)%2;
		if (this.heroicMovePossible(newSelectingArmy)) {
			this.selectingArmy = newSelectingArmy;
			UI.messageLog.add(Game.armyTitles[selectingArmy] + ", choose whether or not to counter the call");
			this.choosingHero = HeroicMoveUI.choosingHeroes.NONE;
			this.counterCall.setVisible(true);
			this.skipCounter.setVisible(true);
		}
		// Otherwise, set to multiple if we have more options
		else if (this.heroicMovePossible(this.selectingArmy)) {
			UI.messageLog.add(Game.armyTitles[newSelectingArmy] + " has no heroes that can counter call");
			UI.messageLog.add(Game.armyTitles[selectingArmy] + ", choose another hero to call or select done");
			this.choosingHero = HeroicMoveUI.choosingHeroes.MULTIPLE;
			this.bothChoosing = false;
			this.doneChoosing.setVisible(true);
		}
		
		// Otherwise, just be done
		else {
			UI.messageLog.add(Game.armyTitles[newSelectingArmy] + " has no heroes that can counter call");
			UI.messageLog.add(Game.armyTitles[selectingArmy] + " is out of heroes that can call a move");
			this.choosingHero = HeroicMoveUI.choosingHeroes.DONE;
			this.doneMoving.setVisible(true);
			this.advanceMovers();
		}
		myUI.repaint();
	}

	public void addCounterCaller(Hero hero) {
		// Assumes counter buttons have been cleaned up
		
		UI.messageLog.add(hero.toChoiceString() + " is calling a heroic move!");
		// Decide who gets to go first
		int firstSide = UI.rng.nextInt(2);
		
		UI.messageLog.add(Game.armyTitles[firstSide] + " has won the coinflip and will be moving first");
		// Add him in depending on who's going to be going first
		if (firstSide == hero.armyIndex){
			this.callingHeroes.add(0, hero);
		}
		else {
			this.callingHeroes.add(hero);
		}
		
		// If the first side can continue, set them as the next to select in multiple
		if (this.heroicMovePossible(firstSide)) {
			this.selectingArmy = firstSide;
			UI.messageLog.add(Game.armyTitles[selectingArmy] + ", choose another hero to call or select done");
			this.choosingHero = HeroicMoveUI.choosingHeroes.MULTIPLE;
			this.doneChoosing.setVisible(true);
		}
		// Otherwise, set to second side if they have more options
		else if (this.heroicMovePossible((firstSide+1)%2)) {
			UI.messageLog.add(Game.armyTitles[firstSide] + " is out of heroes that can call a move");
			this.selectingArmy = (firstSide+1)%2;
			UI.messageLog.add(Game.armyTitles[selectingArmy] + ", choose another hero to call a move or select done");
			this.choosingHero = HeroicMoveUI.choosingHeroes.MULTIPLE;
			this.bothChoosing = false;
			this.doneChoosing.setVisible(true);
		}
		// Otherwise, just be done
		else {
			UI.messageLog.add("Both sides are out of heroes that can call a move");
			this.choosingHero = HeroicMoveUI.choosingHeroes.DONE;
			this.doneMoving.setVisible(true);
			this.advanceMovers();
		}
		myUI.repaint();
	}

	public void addCallingHero(Hero hero) {
		hero.useMight(1);
		this.callingHeroes.add(hero);
		UI.messageLog.add(hero.toChoiceString() + " is calling a heroic move!");
		if (this.bothChoosing) {
			int newSelectingArmy = (this.selectingArmy+1)%2;
			if (this.heroicMovePossible(newSelectingArmy)) {
				this.selectingArmy = newSelectingArmy;
				UI.messageLog.add(Game.armyTitles[selectingArmy] + ", choose another hero to call a move or select done");
			}
			else {
				UI.messageLog.add(Game.armyTitles[newSelectingArmy] + " is out of heroes that can call a move");
				UI.messageLog.add(Game.armyTitles[selectingArmy] + ", choose another hero to call a move or select done");
				this.bothChoosing = false;
				// Not both choosing anymore, continue with this army
			}
		}
		if (!this.heroicMovePossible(this.selectingArmy)) {
			// No more heroes can call, move on to actually moving
			this.choosingHero = HeroicMoveUI.choosingHeroes.DONE;
			this.doneChoosing.setVisible(false);
			this.doneMoving.setVisible(true);
			this.advanceMovers();
		}
		else {
			UI.messageLog.add(Game.armyTitles[selectingArmy] + ", choose another hero to call a move or select done");
		}
	}
	
	public void advanceMovers() {
		myUI.selectedUnit = null;
		myUI.targetedUnit = null;
		if (this.callingHeroes.size() == 0) {
			// Move on to normal move phase
			this.endPhase();
		}
		else {
			Hero nextHero = this.callingHeroes.get(0);
			this.callingHeroes.remove(0);
			this.currentHero = nextHero;
			if (nextHero.wounds <= 0 || nextHero.battle != null) {
				// He died or got caught in battle before getting to call his move
				this.advanceMovers();
			}
			else {
				// We have an actual caller, update the list of units allowed to move
				this.movingUnits = new ArrayList<Unit>();
				for (Unit u : nextHero.army.units){
					if (Utils.inchDist(nextHero, u) <= 6 && u.battle == null)
						this.movingUnits.add(u);
				}
			}
		}
	}
	
	public void endPhase() {
		// Transition to normal move Phase
		// Clean up move stuff
		myUI.removeMouseListener(myMouse);
		myUI.removeMouseMotionListener(myMouse);
		
		// Clean up various Butons and Panels
		if (targetStats != null) {
			myUI.remove(targetStats);
			myUI.remove(callHeroicMove);
			myUI.remove(skipHeroicMove);
			myUI.remove(counterCall);
			myUI.remove(skipCounter);
			myUI.remove(doneChoosing);
			myUI.remove(doneMoving);
		}
		
		if (choosingHero != HeroicMoveUI.choosingHeroes.NONE)
			UI.messageLog.add("Finished Heroic Moves, moving to normal Move Phase");
		
		myUI.endPhase.setEnabled(true);
		myUI.selectedUnit = null;
		myUI.targetedUnit = null;
		
		// Create a moveUI and set it to be the new phase
		MoveUI moveUI = new MoveUI();
		moveUI.myUI = myUI;
		myUI.phaseUI = moveUI;
		myUI.phaseUI.beginPhase();
		myUI.repaint();
	}
	
	public void paint(Graphics bufferG) {
    	if (myUI.targetedUnit != null && myUI.targetedUnit.getWounds() > 0){
    		bufferG.setColor(Color.red);
    		bufferG.drawOval(myUI.targetedUnit.x-(myUI.targetedUnit.pixelRadius+1)-myUI.boardOffsetX, 
    						myUI.targetedUnit.y-(myUI.targetedUnit.pixelRadius+1)-myUI.boardOffsetY, 
    						2*(myUI.targetedUnit.pixelRadius+1), 2*(myUI.targetedUnit.pixelRadius+1));
    		bufferG.setColor(Color.black);
    	}
    	// Draw ovals for units able to move
    	((Graphics2D)bufferG).setStroke(new BasicStroke(2));
		bufferG.setColor(Color.ORANGE);
    	for (Unit u : movingUnits) {
    		bufferG.drawOval(u.x-(u.pixelRadius+2)-myUI.boardOffsetX, u.y-(u.pixelRadius+2)-myUI.boardOffsetY, 
    							2*(u.pixelRadius+2), 2*(u.pixelRadius+2));
    	}
    	bufferG.setColor(Color.BLACK);
		((Graphics2D)bufferG).setStroke(new BasicStroke());
		
		// If showing control zones, do so
    	if (myUI.myKey.isKeyDown('c')) {
			AffineTransform offsetShift = AffineTransform.getTranslateInstance(-1*myUI.boardOffsetX, -1*myUI.boardOffsetY);
			Color goodColor = new Color(0, 255, 0, 50);
			bufferG.setColor(goodColor);
			for (Unit u : myUI.game.armies[Game.GOODINDEX].units) {
    			Area goodClone = u.getControlZone();
    			goodClone.transform(offsetShift);
    			((Graphics2D)bufferG).fill(goodClone);
    		}
			Color badColor = new Color(255, 0, 0, 50);
			bufferG.setColor(badColor);
			for (Unit u : myUI.game.armies[Game.BADINDEX].units) {
    			Area badClone = u.getControlZone();
    			badClone.transform(offsetShift);
    			((Graphics2D)bufferG).fill(badClone);
    		}
    	}
    	bufferG.setColor(Color.BLACK);
		updateTargetPanel();
	}

}
